using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using Microsoft.Office.Interop.Outlook;
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using TXLooker.Common;
using TXLooker.WorkItemExtention.Core;
using TXLooker.WorkItemExtention.Properties;

namespace TXLooker.WorkItemExtention.UI
{
    public partial class CreateWorkItemFromOlItemForm : Form
    {
        List<int> m_listIdNotChange = new List<int>();

        private List<string> m_assignToCC = new List<string>();
        private CreateWorkItemFromOlItemForm()
        {
            InitializeComponent();
            this.m_tfsTreeCtrl.SelectionChanged += new EventHandler(OnTFSSelectionChange);
            this.m_tfsTreeCtrl.LoadF();
            this.m_tfsTreeCtrl.TreeView.ExpandAll();
            this.m_tfsTreeCtrl.TreeView.Sort();
            this.EnableControls(false);
            this.FormClosing += new FormClosingEventHandler(FormClosingEvent);

            this.m_listIdNotChange.AddRange(new int[] {8,25,75,-57,-42,-32,-31,-4,-12,-3,-2,-1});
        }

        void FormClosingEvent(object sender, FormClosingEventArgs e)
        {
            foreach (WorkItemAttachment att in this.m_attachments)
                System.IO.File.Delete(att.FilePath);
        }

        void OnTFSSelectionChange(object sender, EventArgs e)
        {
            Cursor oldCursor = this.Cursor;
            try
            {
                this.Cursor = Cursors.WaitCursor;
                WIObject item = this.m_tfsTreeCtrl.SelectedObject;
                switch (item.Type)
                {
                    case WIObjectType.Server:
                        EnableControls(false);
                        break;
                    case WIObjectType.Project:
                        //List<string> users = GetUsersForProject(item);
                        FillCheckListBox(item);
                        FillWITypeCombo(item);
                        EnableControls(true);
                        break;
                    default: break;
                }
            }
            catch (System.Exception ex)
            {
                LogManager.WriteMessageToCustomLog("Error in OnTFSSelectionChange " + ex.ToString());
            }
            finally
            {
                this.Cursor = oldCursor;
            }
        }

        private List<string> GetUsersForProject(WIObject item)
        {
            List<string> users = new List<string>();
            TeamFoundationServer tfs = item.GetStore().TeamFoundationServer;
            ICommonStructureService css = (ICommonStructureService)tfs.GetService(typeof(ICommonStructureService));
            IGroupSecurityService gss = (IGroupSecurityService)tfs.GetService(typeof(IGroupSecurityService));
            IAuthorizationService auth = (IAuthorizationService)tfs.GetService(typeof(IAuthorizationService));

            ProjectInfo projectInfo = css.GetProjectFromName(WorkItemExtentionGlobal.GetProject(item).Name);

            string objectId = PermissionNamespaces.Project + projectInfo.Uri;
            AccessControlEntry[] aces = auth.ReadAccessControlList(objectId);
            foreach (AccessControlEntry ace in aces)
            {
                Identity aceIdentity = gss.ReadIdentity(SearchFactor.Sid, ace.Sid, QueryMembership.Direct);
                if (aceIdentity.Type == IdentityType.ApplicationGroup)
                {
                    foreach (string s in aceIdentity.Members)
                    {
                        Identity userIdentity = gss.ReadIdentity(SearchFactor.Sid, s, QueryMembership.Direct);
                        if (userIdentity.Type == IdentityType.WindowsUser)
                        {
                            if (users.Contains(userIdentity.DisplayName))
                                continue;
                            if (gss.IsMember(aceIdentity.Sid, userIdentity.Sid))
                                users.Add(userIdentity.DisplayName);
                        }
                    }
                }
            }           

            users.Sort(SortByName);
            return users;
        }

        private int SortByName(string s1, string s2)
        {
            return s1.CompareTo((object)s2);
        }

        private void FillCheckListBox(WIObject item)
        {
            WorkItemStore store = item.GetStore();
            List<string> selectedUsers = new List<string>();
            List<string> allowedUsers = new List<string>();

            //AllowedValuesCollection allowedValuesCollection = item.GetStore().FieldDefinitions[CoreField.AssignedTo].AllowedValues;
            AllowedValuesCollection allowedValuesCollection = null;
            Project project = (item as WIProjectProvider).GetProject();
            foreach (WorkItemType type in project.WorkItemTypes)
            {
                allowedValuesCollection = type.FieldDefinitions[CoreField.AssignedTo].AllowedValues;
                break;
            }

            foreach (string s in m_assignToCC)
                    selectedUsers.Add(s);

            foreach (string s in m_assignTo)
            {
                if (!this.m_assignToCC.Contains(s))
                    selectedUsers.Add(s);
            }

            //foreach (string s in allowedValuesCollection)
            //{
            //    if (users.Contains(s))
            //        allowedUsers.Add(s);
            //}
            if(allowedValuesCollection == null) return;

            foreach (string user in allowedValuesCollection)
                allowedUsers.Add(user);

            this.m_clbAssignTo.DataSource = null;
            this.m_clbAssignTo.DataSource = allowedUsers;
            List<int> checkItems = new List<int>();
            for (int i = 0; i < this.m_clbAssignTo.Items.Count; ++i )
            {
                if (selectedUsers.Contains(this.m_clbAssignTo.Items[i] as string))
                    checkItems.Add(i);
            }

            foreach(int i in checkItems)
                this.m_clbAssignTo.SetItemChecked(i,true);
        }

        private void FillWITypeCombo(WIObject item)
        {
            this.m_comboWiType.DataSource = null;
            Project project = WorkItemExtentionGlobal.GetProject(item);
            this.m_comboWiType.DataSource = project.WorkItemTypes;
            this.m_comboWiType.DisplayMember = "Name";
        }

        private void EnableControls(bool bEnable)
        {
            this.m_btnCreate.Enabled        = bEnable;
            this.m_clbAssignTo.Enabled      = bEnable;
            this.m_comboWiType.Enabled      = bEnable;
            this.m_clbAttachments.Enabled   = bEnable;
            this.m_groupAssignTo.Enabled    = bEnable;
            this.m_groupAttachments.Enabled = bEnable;
        }

        private string m_title;
        private string m_description;
        private List<string> m_assignTo = new List<string>();
        public CreateWorkItemFromOlItemForm(object item, IOlProjectProcessor olProjectProcessor)
            : this()
        {
            this.OlProjectProcessor = olProjectProcessor;
            MailItem mailItem = null;
            TaskItem taskItem = null;
            AppointmentItem appointmentItem = null;
            MeetingItem meetingItem = null;

            if (item is MailItem)
            {
                mailItem = (MailItem)item;
            }
            else if (item is TaskItem)
            {
                taskItem = (TaskItem)item;
            }
            else if (item is AppointmentItem)
            {
                appointmentItem = (AppointmentItem)item;
            }
            else if (item is MeetingItem)
            {
                meetingItem = (MeetingItem)item;
            }

            if (mailItem != null || taskItem != null || appointmentItem != null || meetingItem != null)
            {
                InitFields(mailItem, taskItem, appointmentItem, meetingItem);
            }
        }

        private void InitFields(MailItem mailItem, TaskItem taskItem, AppointmentItem appointmentItem, MeetingItem meetingItem)
        {
            if (mailItem != null)
                InitMailItem(mailItem);
            else if (taskItem != null)
                InitTaskItem(taskItem);
            else if (appointmentItem != null)
                InitAppointmentItem(appointmentItem);
            else if (meetingItem != null)
                InitMeetingItem(meetingItem);
        }

        private void InitMeetingItem(MeetingItem item)
        {
            foreach (Recipient recipient in item.Recipients)
                this.m_assignTo.Add(recipient.Name);
            this.m_title = item.Subject;
            this.m_description = item.Body;
        }

        private void InitTaskItem(TaskItem taskItem)
        {
            foreach (Recipient recipient in taskItem.Recipients)
                this.m_assignTo.Add(recipient.Name);
            this.m_title = taskItem.Subject;
            this.m_description = taskItem.Body;
        }

        private void InitAppointmentItem(AppointmentItem appointmentItem)
        {
            try
            {
                if (appointmentItem.RequiredAttendees != null)
                {
                    foreach (string s in appointmentItem.RequiredAttendees.Split(';'))
                        m_assignTo.Add(s.Trim(' '));
                }
            }
            catch { m_assignTo.Add(appointmentItem.RequiredAttendees); }

            m_title = appointmentItem.Subject;
            m_description = appointmentItem.Body;
        }
        private List<WorkItemAttachment> m_attachments = new List<WorkItemAttachment>();
        private void InitMailItem(MailItem mailItem)
        {
            try
            {
                if (mailItem.CC != null)
                {
                    foreach (string s in mailItem.CC.Split(';'))
                        this.m_assignToCC.Add(s.Trim(' '));
                }
            }
            catch
            {
                this.m_assignToCC.Add(mailItem.CC);
            }

            try
            {
                if (mailItem.To != null)
                {
                    foreach (string s in mailItem.To.Split(';'))
                        this.m_assignTo.Add(s.Trim(' '));
                }
            }
            catch
            {
                this.m_assignTo.Add(mailItem.To);
            }

            this.m_title = mailItem.Subject;
            this.m_description = mailItem.Body;

            SaveAttachments(mailItem);
        }

        private void SaveAttachments(MailItem mailItem)
        {
            for (int i = 1; i <= mailItem.Attachments.Count; i++)
            {
                string filePath = Path.Combine(WorkItemExtentionGlobal.LookerTempFolder, mailItem.Attachments[i].FileName);
                mailItem.Attachments[i].SaveAsFile(filePath);
                this.m_attachments.Add(new WorkItemAttachment(mailItem.Attachments[i].FileName, filePath));
            }

            this.m_clbAttachments.DataSource = this.m_attachments;
            this.m_clbAttachments.DisplayMember = "FileName";
        }

        private IOlProjectProcessor m_olProjectProcessor;

        private IOlProjectProcessor OlProjectProcessor
        {
            get { return this.m_olProjectProcessor; }
            set
            {
                if (object.ReferenceEquals(value, null))
                    throw new NullReferenceException();
                this.m_olProjectProcessor = value;
            }
        }

        private void OnCreateWorkItem(object sender, EventArgs e)
        {
            try
            {
                InitProjectProvider();                
                WorkItem item = null;
                WorkItemType workItemType = this.m_comboWiType.SelectedItem as WorkItemType;

                try { item = workItemType.NewWorkItem(); }
                catch { }

                if (item == null)
                {
                    MessageBox.Show(Resources.WORKITEM_CREATE_ERROR_MESSAGE, Resources.EXTENTION_NAME, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                InitWorkItemFields(ref item);
                InitAttachments(ref item);
              
                string oldAssignTo = null;
                if (this.m_assignTo.Count > 0 && this.m_clbAssignTo.CheckedItems.Count > 0)
                {
                    oldAssignTo = this.m_clbAssignTo.CheckedItems[0].ToString();
                    item.Fields[CoreField.AssignedTo].Value = oldAssignTo;
                }           

                WorkItemForm wiForm = new WorkItemForm(item, this.OlProjectProcessor);
                if (wiForm.ShowDialog(this) == DialogResult.OK)
                {
                    try
                    {
                        item.Save(); 
                    }
                    catch (System.Exception ex)
                    {
                        MessageBox.Show(this,ex.Message,Resources.APPLICATION_NAME,MessageBoxButtons.OK,MessageBoxIcon.Stop);
                        return;
                    }
                    List<string> assignToList = new List<string>();
                    foreach (object o in this.m_clbAssignTo.CheckedItems)
                        assignToList.Add(o.ToString());
                    if (assignToList.Contains(oldAssignTo))
                        assignToList.Remove(oldAssignTo);
                    AttachmentCollection attachmentCol = item.Attachments;
                    CreateWorkItemCopies(item, workItemType, assignToList, attachmentCol);
                }
            }
            catch (System.Exception ex)
            {
                LogManager.WriteMessageToCustomLog("Error in creating work items " + ex.ToString());
            }
        }

        private void InitWorkItemFields(ref WorkItem item)
        {
            item.Title = this.m_title;
            item.Description = this.m_description;
        }

        private List<WorkItemError> m_Errors = new List<WorkItemError>();

        private void InitAttachments(ref WorkItem item)
        {
            foreach (object o in this.m_clbAttachments.CheckedItems)
            {
                try
                {
                    Microsoft.TeamFoundation.WorkItemTracking.Client.Attachment attachment = new Microsoft.TeamFoundation.WorkItemTracking.Client.Attachment((o as WorkItemAttachment).FilePath, string.Empty);
                    item.Attachments.Add(attachment);
                }
                catch(System.Exception ex)
                {
                    this.m_Errors.Add(new WorkItemError(Error.AttachmentError,ex.Message));
                }
            }
        }

        private void CreateWorkItemCopies(WorkItem item, WorkItemType workItemType, List<string> assignToList, AttachmentCollection attachments)
        {
            foreach (string s in assignToList)
            {
                WorkItem workItem = workItemType.NewWorkItem();

                foreach (Field field in item.Fields)
                {
                    try
                    {
                        if (field.Value == null || (string)field.Value == string.Empty) continue;
                        if (this.m_listIdNotChange.Contains(field.Id)) continue;
                        workItem.Fields[field.Name].Value = field.Value;
                    }
                    catch { }
                }
                workItem.Fields[CoreField.AssignedTo].Value = s;
                foreach (Microsoft.TeamFoundation.WorkItemTracking.Client.Attachment attachment in attachments)
                {
                    Microsoft.TeamFoundation.WorkItemTracking.Client.Attachment newAttachment = new Microsoft.TeamFoundation.WorkItemTracking.Client.Attachment(attachment.Uri.LocalPath, attachment.Comment);
                    workItem.Attachments.Add(newAttachment);
                }
                try
                {
                    workItem.Save();
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show(this, ex.Message, Resources.APPLICATION_NAME, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void InitProjectProvider()
        {
            WIProjectProvider provider = null;

            WIObject wiObj = this.m_tfsTreeCtrl.SelectedObject;

            if (wiObj != null)
            {

                switch (wiObj.Type)
                {
                    case WIObjectType.Server:
                        break;
                    case WIObjectType.Project:
                        provider = (wiObj as WIProjectProvider);
                        break;
                }
            }
        }
    }

    #region Helper classes, structures, enums
    enum Error
    { 
        None,
        AttachmentError
    }

    class WorkItemError
    {
        private WorkItemError()
        { }
        private Error m_Error = Error.None;
        private string m_message;
        public WorkItemError(Error error, string Message):this()
        {
            this.Error = error;
            this.Message = Message;
        }

        public Error Error
        {
            get { return this.m_Error; }
            protected set
            { this.m_Error = value; }
        }

        public string Message
        {
            get { return this.m_message; }
            protected set
            { this.m_message = value; }
        }
    }

    class WorkItemAttachment
    {
        private string m_filePath;
        private string m_fileName;

        private WorkItemAttachment()
        { }

        public WorkItemAttachment(string fileName, string filePath)
            : this()
        {
            this.m_fileName = fileName;
            this.m_filePath = filePath;
        }

        public string FileName
        {
            get { return this.m_fileName; }
            set { m_fileName = value; }
        }

        public string FilePath
        {
            get { return m_filePath; }
            set { m_filePath = value; }
        }
    }
    #endregion
}